package org.atlantis.grosbeak.lang.each;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.atlantis.grosbeak.lang.Mirror;

/**
 * 迭代工具类。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
public class Eachs {

	/**
	 * 终止当前的迭代。
	 * 
	 * @throws ExitEach
	 */
	public static void stop() throws ExitEach {
		throw new ExitEach();
	}

	/**
	 * 使用回调的方式，遍历一个对象。支持遍历的对象有：
	 * <ul>
	 * <li>数组</li>
	 * <li>集合</li>
	 * <li>Map</li>
	 * <li>单一元素</li>
	 * </ul>
	 * 
	 * @param obj
	 *            需要执行迭代的对象。
	 * @param callback
	 *            回调操作。
	 */
	@SuppressWarnings("unchecked")
	public static <T> void each(Object obj, Each<T> callback) {
		if (null == obj || null == callback) {
			return;
		}
		Class<T>[] typeParams = (Class<T>[]) Mirror.getClassTypes(callback
				.getClass());
		Class<T> eType = null;
		if (typeParams != null && typeParams.length > 0)
			eType = typeParams[0];
		if (obj.getClass().isArray()) {
			int len = Array.getLength(obj);
			for (int i = 0; i < len; i++)
				try {
					callback.go(i, (T) Array.get(obj, i), len);
				} catch (ExitEach e) {
					break;
				}
		} else if (obj instanceof Collection) {
			int len = ((Collection) obj).size();
			int i = 0;
			for (Iterator<T> iter = ((Collection) obj).iterator(); iter
					.hasNext();) {
				try {
					callback.go(i++, iter.next(), len);
				} catch (ExitEach e) {
					break;
				}
			}
		} else if (obj instanceof Map) {
			Map map = (Map) obj;
			int len = map.size();
			int i = 0;
			if (null != eType && eType.isAssignableFrom(Entry.class)) {
				for (Object v : map.entrySet()) {
					try {
						callback.go(i++, (T) v, len);
					} catch (ExitEach e) {
						break;
					}
				}
			} else {
				for (Object v : map.entrySet()) {
					try {
						callback.go(i++, (T) ((Entry) v).getValue(), len);
					} catch (ExitEach e) {
						break;
					}
				}
			}
		} else {
			try {
				callback.go(0, (T) obj, 1);
			} catch (ExitEach e) {
			}
		}
	}
}
